﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OauthProvider;

namespace Chat_WebServer
{
    public class SimpleTokenStore : ITokenStore
    {
        public IToken CreateRequestToken(IOAuthContext context)
        {
            if (context == null) throw new ArgumentNullException("context");

            var token = new MiniRequestToken
            {
                ConsumerKey = context.ConsumerKey,
                Realm = context.Realm,
                Token = Guid.NewGuid().ToString(),
                TokenSecret = Guid.NewGuid().ToString(),
                CallbackUrl = context.CallbackUrl
            };

            token.SaveToken();

            return token;
        }

        public void ConsumeRequestToken(IOAuthContext requestContext)
        {
            if (requestContext == null) throw new ArgumentNullException("requestContext");

            MiniRequestToken requestToken = GetRequestToken(requestContext);

            UseUpRequestToken(requestContext, requestToken);

            requestToken.SaveToken();
        }

        MiniRequestToken GetRequestToken(IOAuthContext context)
        {
            try
            {
                return (MiniRequestToken)TokenUtility.GetToken(context.Token, TokenType.Request);
            }
            catch (Exception exception)
            {
                // TODO: log exception
                throw Error.UnknownToken(context, context.Token, exception);
            }
        }

        MiniAccessToken GetAccessToken(IOAuthContext context)
        {
            try
            {
                return (MiniAccessToken)TokenUtility.GetToken(context.Token, TokenType.Access);
            }
            catch (Exception exception)
            {
                // TODO: log exception
                throw Error.UnknownToken(context, context.Token, exception);
            }
        }

        public void ConsumeAccessToken(IOAuthContext accessContext)
        {
            MiniAccessToken accessToken = GetAccessToken(accessContext);

            if (accessToken.ExpireyDate < DateTime.Now)
            {
                TokenUtility.RemoveToken(accessToken.Token);
                throw new OAuthException(accessContext, OAuthProblems.TokenExpired,
                                         "Token has expired (they're only valid for 1 hours)");
            }
        }

        public bool RefreshToken(IOAuthContext accessContext)
        {
            MiniRequestToken requestToken = GetRequestTokenContain(accessContext);

            if (requestToken.UsedUp == true && requestToken.AccessToken != null && requestToken.AccessToken.ExpireyDate < DateTime.Now)
            {
                requestToken.AccessToken.ExpireyDate = DateTime.Now.AddHours(1);
                requestToken.SaveToken();
                return true;
            }

            return false;
        }

        private MiniRequestToken GetRequestTokenContain(IOAuthContext accessContext)
        {
            try
            {
                return (MiniRequestToken)TokenUtility.GetRequestTokenWithAccessToken(accessContext.Token);
            }
            catch (Exception exception)
            {
                // TODO: log exception
                throw Error.UnknownToken(accessContext, accessContext.Token, exception);
            }
        }

        public IToken GetAccessTokenAssociatedWithRequestToken(IOAuthContext requestContext)
        {
            MiniRequestToken requestToken = GetRequestToken(requestContext);
            return requestToken.AccessToken;
        }

        public RequestForAccessStatus GetStatusOfRequestForAccess(IOAuthContext accessContext)
        {
            MiniRequestToken request = GetRequestToken(accessContext);

            if (request.AccessDenied) return RequestForAccessStatus.Denied;

            if (request.AccessToken == null) return RequestForAccessStatus.Unknown;

            return RequestForAccessStatus.Granted;
        }

        public string GetCallbackUrlForToken(IOAuthContext requestContext)
        {
            MiniRequestToken requestToken = GetRequestToken(requestContext);
            return requestToken.CallbackUrl;
        }

        public string GetVerificationCodeForRequestToken(IOAuthContext requestContext)
        {
            MiniRequestToken requestToken = GetRequestToken(requestContext);

            return requestToken.Verifier;
        }

        public string GetRequestTokenSecret(IOAuthContext context)
        {
            MiniRequestToken requestToken = GetRequestToken(context);

            return requestToken.TokenSecret;
        }

        public string GetAccessTokenSecret(IOAuthContext context)
        {
            MiniAccessToken token = GetAccessToken(context);

            return token.TokenSecret;
        }

        public IToken GetToken(IOAuthContext context)
        {
            var token = (IToken)null;
            if (!string.IsNullOrEmpty(context.Token))
            {
                try
                {
                    token = TokenUtility.GetToken(context.Token, TokenType.Access) ??
                            TokenUtility.GetToken(context.Token, TokenType.Request);
                }
                catch (Exception ex)
                {
                    // TODO: log exception
                    throw Error.UnknownToken(context, context.Token, ex);
                }

            }
            return token;
        }

        static void UseUpRequestToken(IOAuthContext requestContext, MiniRequestToken requestToken)
        {
            if (requestToken.UsedUp)
            {
                throw new OAuthException(requestContext, OAuthProblems.TokenRejected,
                                         "The request token has already be consumed.");
            }

            requestToken.UsedUp = true;
        }
    }
}
